/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "render_api.h"

#include "IOStream.h"
#include "FrameBuffer.h"
#include "RenderServer.h"
#include "RenderWindow.h"
#include "TimeUtils.h"
#include "Win32PipeStream.h"
#include "EGLDispatch.h"
#include "GLESv1Dispatch.h"
#include "GLESv2Dispatch.h"

#include <string.h>

extern char multiPipeName[];

static RenderServer* s_renderThread = NULL;

static RenderWindow* s_renderWindow = NULL;

static IOStream *createRenderThread(int p_stream_buffer_size,
                                    unsigned int clientFlags);

int initLibrary(void)
{
    //
    // Load EGL Plugin
    //
    if (!init_egl_dispatch()) {
        // Failed to load EGL
		ERR("Failed to init_egl_dispatch\n");
        return false;
    }

    //
    // Load GLES Plugin
    //
    if (!init_gles1_dispatch()) {
        // Failed to load GLES
        ERR("Failed to init_gles1_dispatch\n");
        return false;
    }

    /* failure to init the GLES2 dispatch table is not fatal */
    if (!init_gles2_dispatch()) {
        ERR("Failed to init_gles2_dispatch\n");
        return false;
    }

    return true;
}


int initOpenGLRenderer(int width, int height)
{
    //
    // Fail if renderer is already initialized
    //
    if (s_renderThread) {
        return false;
    }

    // kUseThread is used to determine whether the RenderWindow should use
    // a separate thread to manage its subwindow GL/GLES context.
    // Experience shows that:
    //
    // - It is necessary on Linux/XGL and OSX/Cocoa to avoid corruption
    //   issues with the GL state of the main window, resulting in garbage
    //   or black content of the non-framebuffer UI parts.
    //
    // - It must be disabled on Windows, otherwise the main window becomes
    //   unresponsive after a few seconds of user interaction (e.g. trying to
    //   move it over the desktop). Probably due to the subtle issues around
    //   input on this platform (input-queue is global, message-queue is
    //   per-thread). Also, this messes considerably the display of the
    //   main window when running the executable under Wine.
    //
    //
    // initialize the renderer and listen to connections
    // on a thread in the current process.
    //
	s_renderWindow = new RenderWindow(width, height, false);// bool kUseThread = false;
    if (!s_renderWindow) {
        ERR("Could not create rendering window class");
        return false;
    }
    if (!s_renderWindow->isValid()) {
        ERR("Could not initialize emulated framebuffer");
        delete s_renderWindow;
        return false;
    }

    s_renderThread = RenderServer::create();
    if (!s_renderThread) {
        return false;
    }
    s_renderThread->start();

    return true;
}

void setPostCallback(OnPostFn onPost, void* onPostContext)
{
    if (s_renderWindow) {
        s_renderWindow->setPostCallback(onPost, onPostContext);
    } else {
        ERR("Calling setPostCallback() before creating render window!");
    }
}

void getHardwareStrings(const char** vendor,
                        const char** renderer,
                        const char** version) {
    if (s_renderWindow &&
        s_renderWindow->getHardwareStrings(vendor, renderer, version)) {
        return;
    }
    *vendor = *renderer = *version = NULL;
}

int stopOpenGLRenderer(void)
{
    bool ret = false;

    // open a dummy connection to the renderer to make it
    // realize the exit request.
    // (send the exit request in clientFlags)
    IOStream *dummy = createRenderThread(8, IOSTREAM_CLIENT_EXIT_SERVER);
    if (!dummy) return false;

    if (s_renderThread) {
        // wait for the thread to exit
        ret = s_renderThread->wait(NULL);

        //delete s_renderThread;
        //s_renderThread = NULL;
    }

    if (s_renderWindow) {
        delete s_renderWindow;
        s_renderWindow = NULL;
    }

    return ret;
}

bool createOpenGLSubwindow(HWND window_id,
                           int x,
                           int y,
                           int width,
                           int height,
                           float zRot)
{
    RenderWindow* window = s_renderWindow;

    if (window) {
       return window->setupSubWindow(window_id,x,y,width,height, zRot);
    }
    // XXX: should be implemented by sending the renderer process
    //      a request
    ERR("%s not implemented for separate renderer process !!!\n",
        __FUNCTION__);
    return false;
}

static void (* fn_callback_rotation)(float) = NULL;

void setCallbackRotation(void (* fn)(float)) {
    fn_callback_rotation = fn;
}

void callbackRotation(float zRot) {
    if (fn_callback_rotation)
        (*fn_callback_rotation)(zRot);
}

bool destroyOpenGLSubwindow(void)
{
    RenderWindow* window = s_renderWindow;

    if (window) {
        return window->removeSubWindow();
    }

    // XXX: should be implemented by sending the renderer process
    //      a request
    ERR("%s not implemented for separate renderer process !!!\n",
            __FUNCTION__);
    return false;
}

void setOpenGLDisplayRotation(float zRot)
{
    RenderWindow* window = s_renderWindow;

    if (window) {
        window->setRotation(zRot);
        return;
    }
    // XXX: should be implemented by sending the renderer process
    //      a request
    ERR("%s not implemented for separate renderer process !!!\n",
            __FUNCTION__);
}

void repaintOpenGLDisplay(void)
{
    RenderWindow* window = s_renderWindow;

    if (window) {
        window->repaint();
        return;
    }


	FrameBuffer *fb = FrameBuffer::getFB();
	if (fb) {
		fb->repost();
	}
    // XXX: should be implemented by sending the renderer process
    //      a request
    ERR("%s not implemented for separate renderer process !!!\n",
            __FUNCTION__);
}

IOStream *createRenderThread(int p_stream_buffer_size, unsigned int clientFlags)
{
    SocketStream*  stream = NULL;
	stream = new Win32PipeStream(p_stream_buffer_size);
    if (!stream) {
        ERR("createRenderThread failed to create stream\n");
        return NULL;
    }
    if (stream->connect() < 0) {
        ERR("createRenderThread failed to connect\n");
        delete stream;
        return NULL;
    }

    //
    // send clientFlags to the renderer
    //
    unsigned int *pClientFlags = (unsigned int *)stream->allocBuffer(sizeof(unsigned int));
    *pClientFlags = clientFlags;
    stream->commitBuffer(sizeof(unsigned int));

    return stream;
}

int FrameBuffer_setupSubWindow(void *id, int w, int h, float zrot)
{
	return FrameBuffer::setupSubWindow((HWND)id, 0, 0, w, h, zrot);
}

int FrameBuffer_removeSubWindow() {
	return FrameBuffer::removeSubWindow();
}

unsigned char*  Screenshot() {
	FrameBuffer *fb = FrameBuffer::getFB();
	return fb->Screenshot();
}

int setPipename(char *pipe)
{
	_snprintf(multiPipeName, 256, "\\\\.\\pipe\\%s_gles", pipe);
	return 0;
}
